package org.dfzt.modules.system.controller;

import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.shiro.SecurityUtils;
import org.dfzt.common.api.vo.Result;
import org.dfzt.common.constant.CacheConstant;
import org.dfzt.common.constant.CommonConstant;
import org.dfzt.common.system.service.ISysBaseAPI;
import org.dfzt.common.system.util.JwtUtil;
import org.dfzt.common.system.vo.LoginUser;
import org.dfzt.common.util.MD5Util;
import org.dfzt.common.util.RedisUtil;
import org.dfzt.common.util.encryption.EncryptedString;
import org.dfzt.common.util.encryption.SMUtils;
import org.dfzt.common.util.oConvertUtils;
import org.dfzt.modules.message.service.SendMessageService;
import org.dfzt.modules.system.entity.SysDepart;
import org.dfzt.modules.system.entity.SysLog;
import org.dfzt.modules.system.entity.SysUser;
import org.dfzt.modules.system.entity.SysUserRole;
import org.dfzt.modules.system.model.SysLoginModel;
import org.dfzt.modules.system.service.*;
import org.dfzt.modules.system.util.RandImageUtil;
import org.dfzt.modules.system.util.RandomCodeUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;

//import org.dfzt.modules.shiro.vo.DefContants;

/**
 * @Author scott
 * @since 2018-12-17
 */
@RestController
@RequestMapping("/sys")
@Api(tags="用户登录")

public class LoginController {
	private static final Log log = LogFactory.getLog(LoginController.class);
	@Autowired
	private ISysUserService sysUserService;
	@Autowired
	private ISysBaseAPI sysBaseAPI;
	@Autowired
	private ISysLogService logService;
	@Autowired
	private RedisUtil redisUtil;
	@Autowired
	private ISysDepartService sysDepartService;
	@Autowired
	private ISysDictService sysDictService;
	@Autowired
    private SendMessageService sendMessageService;
	@Autowired
    private ISysUserRoleService sysUserRoleService;
	@Value(value = "${verifyCaptcha:true}")
    private boolean verifyCaptcha;

	@Value(value = "${adminRoleId:f6817f48af4fb3af11b9e8bf182f618b}")
	private String adminRoleId;

	@ApiOperation("登录接口")
	@RequestMapping(value = "/login", method = RequestMethod.POST)
	public Result<JSONObject> login(@RequestBody SysLoginModel sysLoginModel,HttpServletRequest req){
		Result<JSONObject> result = new Result<>();
		String captcha = sysLoginModel.getCaptcha();
		if(captcha==null){
			result.error500("验证码无效");
			return result;
		}
		String lowerCaseCaptcha = captcha.toLowerCase();
		String realKey = MD5Util.MD5Encode(lowerCaseCaptcha+sysLoginModel.getCheckKey(), "utf-8");
		Object checkCode = redisUtil.get(realKey);
		if(verifyCaptcha&&(checkCode==null || !checkCode.equals(lowerCaseCaptcha))) {
			result.error500("验证码错误");
			return result;
		}
		return localLogin(sysLoginModel, req);
	}

	/**
	 * 退出登录
	 * @param request
	 * @return
	 */
	@RequestMapping(value = "/logout")
	public Result<Object> logout(HttpServletRequest request) {
		//用户退出逻辑
		String token = request.getHeader("X-Access-Token");
		if(oConvertUtils.isEmpty(token)) {
			return Result.error("退出登录失败！");
		}
		String username = JwtUtil.getUsername(token);
		LoginUser sysUser = sysBaseAPI.getUserByName(username);
		if(sysUser!=null) {
			sysBaseAPI.addLog(sysUser.getRealname() + "退出成功！",
					CommonConstant.TYPE_SYSTEM, CommonConstant.OPERATE_TYPE_LOGOUT, CommonConstant.FLAG_SUCCESS,sysUser.getUsername());
			log.info(" 用户名:  " + sysUser.getRealname() + ",退出成功！ ");
			// 清空用户Token缓存
			redisUtil.del(sysUser.getUsername()+"_"+token);
			//清空用户登录Shiro权限缓存
			redisUtil.del(CommonConstant.PREFIX_USER_SHIRO_CACHE + sysUser.getId());
			//清空用户的缓存信息（包括部门信息），例如sys:cache:user::<username>
			redisUtil.del(String.format("%s::%s", CacheConstant.SYS_USERS_CACHE, sysUser.getUsername()));
			//调用shiro的logout
			SecurityUtils.getSubject().logout();
			return Result.ok("退出登录成功！");
		}else {
			return Result.error("Token无效!");
		}
	}

	/**
	 * 获取访问量
	 * @return
	 */
	@GetMapping("loginfo")
	public Result<JSONObject> loginfo() {
		Result<JSONObject> result = new Result<JSONObject>();
		JSONObject obj = new JSONObject();
		//update-begin--Author:zhangweijian  Date:20190428 for：传入开始时间，结束时间参数
		// 获取一天的开始和结束时间
		Calendar calendar = new GregorianCalendar();
		calendar.set(Calendar.HOUR_OF_DAY, 0);
		calendar.set(Calendar.MINUTE, 0);
		calendar.set(Calendar.SECOND, 0);
		calendar.set(Calendar.MILLISECOND, 0);
		Date dayStart = calendar.getTime();
		calendar.add(Calendar.DATE, 1);
		Date dayEnd = calendar.getTime();
		// 获取系统访问记录
		Long totalVisitCount = logService.findTotalVisitCount();
		obj.put("totalVisitCount", totalVisitCount);
		Long todayVisitCount = logService.findTodayVisitCount(dayStart,dayEnd);
		obj.put("todayVisitCount", todayVisitCount);
		Long todayIp = logService.findTodayIp(dayStart,dayEnd);
		//update-end--Author:zhangweijian  Date:20190428 for：传入开始时间，结束时间参数
		obj.put("todayIp", todayIp);
		result.setResult(obj);
		result.success("登录成功");
		return result;
	}

	/**
	 * 获取访问量
	 * @return
	 */
	@GetMapping("visitInfo")
	public Result<List<Map<String,Object>>> visitInfo() {
		Result<List<Map<String,Object>>> result = new Result<List<Map<String,Object>>>();
		Calendar calendar = new GregorianCalendar();
		calendar.set(Calendar.HOUR_OF_DAY,0);
		calendar.set(Calendar.MINUTE,0);
		calendar.set(Calendar.SECOND,0);
		calendar.set(Calendar.MILLISECOND,0);
		calendar.add(Calendar.DAY_OF_MONTH, 1);
		Date dayEnd = calendar.getTime();
		calendar.add(Calendar.DAY_OF_MONTH, -7);
		Date dayStart = calendar.getTime();
		List<Map<String,Object>> list = logService.findVisitCount(dayStart, dayEnd);
		result.setResult(oConvertUtils.toLowerCasePageList(list));
		return result;
	}


	/**
	 * 登陆成功选择用户当前部门
	 * @param user
	 * @return
	 */
	@RequestMapping(value = "/selectDepart", method = RequestMethod.POST)
	public Result<JSONObject> selectDepart(@RequestBody SysUser user) {
		Result<JSONObject> result = new Result<JSONObject>();
		String username = user.getUsername();
		if(oConvertUtils.isEmpty(username)) {
			LoginUser sysUser = (LoginUser)SecurityUtils.getSubject().getPrincipal();
			username = sysUser.getUsername();
		}
		String orgCode= user.getOrgCode();
		this.sysUserService.updateUserDepart(username, orgCode);
		SysUser sysUser = sysUserService.getUserByName(username);
		JSONObject obj = new JSONObject();
		obj.put("userInfo", sysUser);
		result.setResult(obj);
		return result;
	}

	/**
	 * 短信登录接口
	 *
	 * @param jsonObject
	 * @return
	 */
	@PostMapping(value = "/sms")
	public Result<String> sms(@RequestBody JSONObject jsonObject) {
		Result<String> result = new Result<String>();
		String phone = jsonObject.getString("phone");
		//手机号模式 登录模式: "2"  注册模式: "1"
		String smsmode=jsonObject.getString("smsmode");
		log.info(phone);
		if(oConvertUtils.isEmpty(phone)){
			result.setMessage("手机号不允许为空！");
			result.setSuccess(false);
			return result;
		}
		Object object = redisUtil.get(phone);
		if (object != null) {
			result.setMessage("验证码10分钟内，仍然有效！");
			result.setSuccess(false);
			return result;
		}

		//随机数
		//String captcha = RandomUtil.randomNumbers(6);
		String captcha = "123456";
		JSONObject obj = new JSONObject();
		obj.put("code", captcha);
		//try {
		boolean b = false;
		//注册模板
		if (CommonConstant.SMS_TPL_TYPE_1.equals(smsmode)) {
			SysUser sysUser = sysUserService.getUserByPhone(phone);
			if(sysUser!=null) {
				result.error500(" 手机号已经注册，请直接登录！");
				return result;
			}
			b=true;
			//b = DySmsHelper.sendSms(mobile, obj, DySmsEnum.REGISTER_TEMPLATE_CODE);
		}else {
			//登录模式，校验用户有效性
			SysUser sysUser = sysUserService.getUserByPhone(phone);
			result = sysUserService.checkUserIsEffective(sysUser);
			if(!result.isSuccess()) {
				return result;
			}

			/**
			 * smsmode 短信模板方式  0 .登录模板、1.注册模板、2.忘记密码模板
			 */
			if (CommonConstant.SMS_TPL_TYPE_0.equals(smsmode)) {
				//登录模板
				//b = DySmsHelper.sendSms(mobile, obj, DySmsEnum.LOGIN_TEMPLATE_CODE);
			} else if(CommonConstant.SMS_TPL_TYPE_2.equals(smsmode)) {
				//忘记密码模板
				//b = DySmsHelper.sendSms(mobile, obj, DySmsEnum.FORGET_PASSWORD_TEMPLATE_CODE);
			}
			b=true;
		}

		if (!b) {
			result.setMessage("短信验证码发送失败,请稍后重试");
			result.setSuccess(false);
			return result;
		}
		//验证码10分钟内有效
		redisUtil.set(phone, captcha, 600);
		//update-begin--Author:scott  Date:20190812 for：issues#391
		//result.setResult(captcha);
		//update-end--Author:scott  Date:20190812 for：issues#391
		result.setSuccess(true);

//		} catch (ClientException e) {
//			e.printStackTrace();
//			result.error500(" 短信接口未配置，请联系管理员！");
//			return result;
//		}
		return result;
	}


	/**
	 * 手机号登录接口
	 *
	 * @param jsonObject
	 * @return
	 */
	@ApiOperation("手机号登录接口")
	@PostMapping("/phoneLogin")
	public Result<JSONObject> phoneLogin(@RequestBody JSONObject jsonObject) {
		Result<JSONObject> result = new Result<JSONObject>();
		String phone = jsonObject.getString("mobile");

		//校验用户有效性
		SysUser sysUser = sysUserService.getUserByPhone(phone);
		result = sysUserService.checkUserIsEffective(sysUser);
		if(!result.isSuccess()) {
			return result;
		}

		String smscode = jsonObject.getString("captcha");
		Object code = redisUtil.get(phone);
		if (!smscode.equals(code)) {
			result.setMessage("手机验证码错误");
			return result;
		}
		//用户信息
		userInfo(sysUser, result);
		//添加日志
		sysBaseAPI.addLog(sysUser.getUsername() + ",登录成功！",
				CommonConstant.TYPE_SYSTEM, CommonConstant.OPERATE_TYPE_LOGIN, CommonConstant.FLAG_SUCCESS,sysUser.getUsername());
		return result;
	}

	/**
	 * 获取加密字符串
	 * @return
	 */
	@GetMapping(value = "/getEncryptedString")
	public Result<Map<String,String>> getEncryptedString(){
		Result<Map<String,String>> result = new Result<Map<String,String>>();
		Map<String,String> map = new HashMap<String,String>();
		map.put("key", EncryptedString.key);
		map.put("iv",EncryptedString.iv);
		result.setResult(map);
		return result;
	}

	/**
	 * 后台生成图形验证码 ：有效
	 * @param response
	 * @param key
	 */
	@ApiOperation("获取验证码")
	@GetMapping(value = "/randomImage/{key}")
	public Result<String> randomImage(HttpServletResponse response,@PathVariable String key){
		Result<String> res = new Result<String>();
		try {
			String code = RandomCodeUtil.randomCode();
			String lowerCaseCode = code.toLowerCase();
			String realKey = MD5Util.MD5Encode(lowerCaseCode+key, "utf-8");
			redisUtil.set(realKey, lowerCaseCode, 60);
			String base64 = RandImageUtil.generate(code);
			res.setSuccess(true);
			res.setResult(base64);
		} catch (Exception e) {
			res.error500("获取验证码出错"+e.getMessage());
			log.error(e);
		}
		return res;
	}

	/**
	 * 图形验证码
	 * @param sysLoginModel
	 * @return
	 */
	@RequestMapping(value = "/checkCaptcha", method = RequestMethod.POST)
	public Result<?> checkCaptcha(@RequestBody SysLoginModel sysLoginModel){
		String captcha = sysLoginModel.getCaptcha();
		String checkKey = sysLoginModel.getCheckKey();
		if(captcha==null){
			return Result.error("验证码无效");
		}
		String lowerCaseCaptcha = captcha.toLowerCase();
		String realKey = MD5Util.MD5Encode(lowerCaseCaptcha+checkKey, "utf-8");
		Object checkCode = redisUtil.get(realKey);
		if(checkCode==null || !checkCode.equals(lowerCaseCaptcha)) {
			return Result.error("验证码错误");
		}
		return Result.ok();
	}



	private Result<JSONObject> localLogin(SysLoginModel sysLoginModel,HttpServletRequest req){
		Result<JSONObject> result = new Result<>();
		//解密操作
		String userNameKey = SMUtils.sm2Decrypt(sysLoginModel.getUsername());
		String passWordKey = SMUtils.sm2Decrypt(sysLoginModel.getPassword());
		//每次登陆都需先判断是否被锁定
		if (redisUtil.hasKey(userNameKey) && String.valueOf(redisUtil.get(userNameKey)).equals("lock")){
			result.error500("账户已被锁定,请一小时后重试");
			sysBaseAPI.addLog("账号已被锁定，用户名:" + userNameKey,
					CommonConstant.TYPE_SYSTEM, CommonConstant.OPERATE_TYPE_LOGIN, CommonConstant.FLAG_FAIL,userNameKey);
			return result;
		}
		SysUser sysUser = sysUserService.getUserByName(userNameKey);
		if (sysUser == null) {
			sysBaseAPI.addLog(userNameKey+"登录失败",
					CommonConstant.TYPE_SYSTEM, CommonConstant.OPERATE_TYPE_LOGIN, CommonConstant.FLAG_FAIL,userNameKey);
			return checkRedisError(userNameKey);
		} else {
            if ("1".equals(sysUser.getNeedUpdatePw())){
                result.setCode(0);
                result.setSuccess(false);
                result.setMessage("当前用户为第一次登录，需修改密码");
                return result;
            }
		    if (!checkLoginTime(sysUser)){
                result.setCode(500);
                result.setSuccess(false);
                result.setMessage("当前用户访问不在规定时间段内，请联系管理员！");
                return result;
            }
			if(sysUser.getStatus()!=1){
			    switch (sysUser.getStatus()){
                    case 2:
                        result.error500("当前用户已冻结");
                        break;
                    case 3:
                        result.error500("当前用户已休眠");
                        break;
                    case 4:
                        result.error500("当前用户已注销");
                        break;
                    default:
                        result.error500("当前账户不可登陆！请联系管理员处理");
                        break;
                }
				sysBaseAPI.addLog(userNameKey+"登录失败",
                        CommonConstant.TYPE_SYSTEM, CommonConstant.OPERATE_TYPE_LOGIN, CommonConstant.FLAG_FAIL,userNameKey);
				return result;
			}
			//密码验证
			String sysPassWord = sysUser.getPassword();
			//比较两个密码
			if(!SMUtils.compareTwoPWD(sysPassWord,passWordKey)){
                sysBaseAPI.addLog(userNameKey+"登录失败",
                        CommonConstant.TYPE_SYSTEM, CommonConstant.OPERATE_TYPE_LOGIN, CommonConstant.FLAG_FAIL,userNameKey);
				return checkRedisError(userNameKey);
			}
			// 获取用户部门信息
			userInfo(sysUser, result);
			SysLog lastLoginIP = logService.getLastLogin(userNameKey);
			if(lastLoginIP==null){
				result.success(null);
			}else{
				String thisIP=oConvertUtils.getIpAddrByRequest(req);
				String lastIP=lastLoginIP.getIp();
				if(!oConvertUtils.sameNetworkArea(thisIP,lastIP)){
					result.success("---此次登陆IP变动过大！" +
							"---本次登陆IP：" +thisIP+
							"---上次登陆IP："+lastIP);
				}else {
					result.success(null);
				}
			}
			//成功判断是否存在redis存在则删除
			if (redisUtil.hasKey(userNameKey)){
				redisUtil.del(userNameKey);
			}
			sysBaseAPI.addLog(userNameKey + ",登录成功！",
					CommonConstant.TYPE_SYSTEM, CommonConstant.OPERATE_TYPE_LOGIN, CommonConstant.FLAG_SUCCESS,userNameKey);
		}
		return result;
	}

	private boolean checkLoginTime(SysUser user){
        String time = user.getUserLoginTimeQuantum();
        if (StringUtils.isBlank(time)){
            return false;
        }
        String[] split = time.split("~");
        String nowTime = DateUtil.now();
        String nowStr = nowTime.substring(0,nowTime.indexOf(" "));
        String startTime = nowStr + " " + split[0];
        String endTime = nowStr + " " + split[1];
        Date startDate = DateUtil.parse(startTime);
        Date endDate = DateUtil.parse(endTime);

        Calendar date = Calendar.getInstance();
        date.setTime(DateUtil.parse(nowTime));

        Calendar begin = Calendar.getInstance();
        begin.setTime(startDate);

        Calendar end = Calendar.getInstance();
        end.setTime(endDate);

        if (date.after(begin) && date.before(end)) {
            return true;
        } else {
            return false;
        }
    }

	// 密码错误提供redis锁定该用户,单独抽取
	private Result<JSONObject> checkRedisError(String userNameKey) {
		Result<JSONObject> result = new Result<>();

		if (redisUtil.hasKey(userNameKey)){
            if ((Integer)redisUtil.get(userNameKey)<JwtUtil.MAX_LOCK_TIMES){
				redisUtil.incr(userNameKey,1);
			}else{
				redisUtil.set(userNameKey,"lock",JwtUtil.LOCK_TIME*60);
                List<SysUserRole> list = sysUserRoleService.list(new LambdaQueryWrapper<SysUserRole>()
                        .eq(SysUserRole::getRoleId, adminRoleId));
                StringBuilder builder = new StringBuilder();
                list.forEach(i -> {
                    SysUser user = sysUserService.getById(i.getUserId());
                    if (user!=null){
                        builder.append(user.getUsername());
                        builder.append(",");
                    }
                });
                builder.deleteCharAt(builder.lastIndexOf(","));
                sendMessageService.sendSysAnnouncement("system", builder.toString(),"系统通知",userNameKey+"登录失败，已被锁定");
				result.error500("账户已被锁定,请一小时后重试");
				return result;
            }
		}else{
			redisUtil.set(userNameKey,1,JwtUtil.CLEAR_TIME*60);
		}
		result.error500("用户名或密码错误");
		return result;
	}

	/**
	 * 用户信息
	 *
	 * @param sysUser
	 * @param result
	 * @return
	 */
	private Result<JSONObject> userInfo(SysUser sysUser, Result<JSONObject> result) {
		String syspassword = sysUser.getPassword();
		String username = sysUser.getUsername();
		// 生成token
		String token = JwtUtil.sign(username, syspassword);
		String key=username+"_"+token;
		// 设置token缓存有效时间
		redisUtil.set(key, token);
		redisUtil.expire(key, JwtUtil.EXPIRE_TIME/1000+300);

		// 获取用户部门信息
		JSONObject obj = new JSONObject();
		List<SysDepart> departs = sysDepartService.queryUserDeparts(sysUser.getId());
		obj.put("departs", departs);
		if (departs == null || departs.size() == 0) {
			obj.put("multi_depart", 0);
		} else if (departs.size() == 1) {
			sysUserService.updateUserDepart(username, departs.get(0).getId());
			obj.put("multi_depart", 1);
		} else {
			obj.put("multi_depart", 2);
		}
		obj.put("token", token);
		obj.put("userInfo", JSONObject.parseObject(JSON.toJSONString(sysUser),LoginUser.class));
		obj.put("sysAllDictItems", sysDictService.queryAllDictItems());
		result.setResult(obj);
		result.success("登录成功");
		return result;
	}

}